package com.sunnada.poi.word;

import org.apache.poi.xwpf.usermodel.*;
import org.junit.jupiter.api.Test;

import java.io.*;
import java.util.*;

/**
 * @BelongsProject: poi-extended
 * @BelongsPackage: com.sunnada.poi.word
 * @Author: 李瑞益
 * @CreateTime: 2018-10-17 14:00
 * @Description: 替换Word模板中的变量
 */
public class RepleaceWordModel {

    /**对应文档中的变量名前缀:如<<name>>中的“<<”</>*/
    private String prefix = "«";
//    private String prefix = "《";

    /**对应文档中的变量名后缀:如<<name>>中的“>>”*/
    private String suffix = "»";
//    private String suffix = "》";

    /**设置文档中遍历的前缀:如<<name>>中的“<<”*/
    public void setPrefix(String prefix){
        this.prefix = prefix;
    }
    /**设置文档中遍历的后缀:如<<name>>中的“<<”*/
    public void setSuffix(String suffix){
        this.suffix = suffix;
    }
    /**设置文档中遍历的前缀和后缀:如<<name>>中的“<<”和“>>”*/
    public void setPrefixAndSuffix(String prefix,String suffix){
        this.prefix = prefix;
        this.suffix = suffix;
    }
    @Test
    public void repleace() {
        //变量
        Map<String,Object> map = new HashMap<>();
        map.put("CompanyName","瑞益有限责任公司");
        map.put("Type","计算机");
        map.put("Number","05120213A");

        String readFilePath = "D:\\ProgramData\\Gitee Pro\\poiextended\\src\\main\\resources\\不予受理通知书.docx";
        String writeFilePath ="D:\\ProgramData\\Gitee Pro\\poiextended\\src\\main\\resources\\completeword.docx";
//        replaceOperation(readFilePath,writeFilePath,map);
        byte[] data = getReplaceData(readFilePath, map);
        System.out.println(Arrays.toString(data));
    }


    /**
     * 传入模板文件对象、目标文件对象和变量集合 生成我们需要的Word
     * @param readFilePath 要读入的模板文件路径
     * @param writeFilePath 写出的目标路径(不一定要在改路径创建文件）
     * @param map 待替换的键值
     */
    public void replaceOperation(String readFilePath,String writeFilePath,Map<String,Object> map){

        try {
            XWPFDocument document = new XWPFDocument(XWPFDocument.openPackage(readFilePath));
            //遍历并替换文档中的值
            iterParagraphAndReplaceVariable(document,map);
            File file = new File(writeFilePath);
            //写出文件不存在时创建改文件
            if(!file.exists()){
                file.createNewFile();
            }
            OutputStream os = new BufferedOutputStream(new FileOutputStream(writeFilePath));
            document.write(os);
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 传入模板文件路径和待替换的键值，获取替换完成后的二进制数据
     *
     * @param readFilePath 文件读取路径
     * @param map 待替换的键值
     * @return
     */
    public byte[] getReplaceData(String readFilePath,Map<String,Object> map){
        ByteArrayOutputStream os = null;
        try {
            XWPFDocument document = new XWPFDocument(XWPFDocument.openPackage(readFilePath));
            //遍历并替换文档中的值
            iterParagraphAndReplaceVariable(document,map);
            os = new ByteArrayOutputStream();
            document.write(os);
            return os.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 重载方法：传入模板文件对象、目标文件对象和变量集合 生成我们需要的Word
     * @param readFile
     * @param writeFile
     * @param map
     */
    public void replaceOperation(File readFile,File writeFile,Map<String,Object> map){
        try {
            XWPFDocument document = new XWPFDocument(new BufferedInputStream(new FileInputStream(readFile)));
            //遍历并替换文档中的值
            iterParagraphAndReplaceVariable(document,map);
            //写出文件不存在时创建改文件
            if(!writeFile.exists()){
                writeFile.createNewFile();
            }
            OutputStream os = new BufferedOutputStream(new FileOutputStream(writeFile));
            document.write(os);
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 传入遍历集合，设置变量名和文档中对应;如：key=companyName -》 key=《companyName》
     * 但是已在比对中增加了前后缀，所以这个方法暂时不被使用
     * @param map
     * @return
     */
    private Map<String,Object> setVariableName(Map<String,Object> map){
        Iterator<String> keyIter = map.keySet().iterator();
        Map<String,Object> newMap = new HashMap<>();
        while(keyIter.hasNext()){
            String key = keyIter.next();
            newMap.put(prefix+key+suffix,map.get(key));
        }
        return newMap;
    }
    /**
     *传入文件对象和参数列表进行遍历
     * @param document docx文件的根
     * @param params 变量参数
     */
    private void iterParagraphAndReplaceVariable(XWPFDocument document, Map<String,Object> params) {
        //遍历段落
        Iterator<XWPFParagraph> paragraphsIterator = document.getParagraphsIterator();//获取的段落的迭代器
        while(paragraphsIterator.hasNext()){
            XWPFParagraph paragraph = paragraphsIterator.next();
            //替换段落里的变量
            replaceVariable( paragraph,params);
        }
        //遍历表格
        Iterator<XWPFTable> tablesIterator = document.getTablesIterator();
        while(tablesIterator.hasNext()){
            XWPFTable table = tablesIterator.next();
            replaceVariable(table,params);
        }
    }

    /**
     * 传入段落对象
     * 1、遍历文本区域
     * 2、遍历参数列表
     * 3、比对
     * @param paragraph
     * @param params
     */
    private void replaceVariable(XWPFParagraph paragraph, Map<String,Object> params){
        //文本区域
        List<XWPFRun> runList = paragraph.getRuns();
        Iterator<String> iter = params.keySet().iterator();
        //遍历遍历参数
        while(iter.hasNext()){
            String key = iter.next();//变量名
            //遍历文本区域
            for(int i = 0; i < runList.size() ; i++){
                if(runList.get(i).getText(runList.get(i).getTextPosition()) != null &&
                        runList.get(i).getText(runList.get(i).getTextPosition())
                                .equalsIgnoreCase(prefix+key+suffix)){
                    //从0开始替换变量
                    runList.get(i).setText(params.get(key).toString(),0);
                }
            }
        }
    }

    /**
     * 传入表格对象
     * 1、遍历表格行
     * 2、遍历行的单元格
     * 3、比对
     * @param table
     * @param params
     */
    private void replaceVariable(XWPFTable table,Map<String,Object> params){
        List<XWPFTableRow> rows = table.getRows();
        //遍历行
        for(XWPFTableRow row :rows){
            List<XWPFTableCell> cells = row.getTableCells();
            //遍历单元格
            for(XWPFTableCell cell : cells){
                List<XWPFParagraph> paragraphs = cell.getParagraphs();
                //遍历单元格里的段落
                for(XWPFParagraph paragraph : paragraphs){
                    replaceVariable(paragraph,params);
                }
            }
        }
    }
}
